﻿using System;
using System.Collections.Generic;
using System.Threading;
using Cjc.ExpressionEngine.Excel.Compiler;
using Cjc.ExpressionEngine.Excel.Runtime.Binding;
using Microsoft.Linq.Expressions;
using Microsoft.Scripting;
using Microsoft.Scripting.Actions;
using Microsoft.Scripting.Ast;
using Microsoft.Scripting.Runtime;

namespace Cjc.ExpressionEngine.Excel
{
	public class ExcelContext : LanguageContext
	{
		private static ExcelBinder binder;

		public ExcelContext( ScriptDomainManager manager, IDictionary<string, object> options )
			: base( manager )
		{
			Interlocked.CompareExchange<ExcelBinder>( ref binder, new ExcelBinder( manager ), null );
			Binder = binder;
		}

		protected override ScriptCode CompileSourceCode( SourceUnit sourceUnit, CompilerOptions options, ErrorSink errorSink )
		{
			var lambda = ParseSourceCode( sourceUnit, options, errorSink );

			if ( lambda == null ) return null;

			return new OptimizedScriptCode( lambda, sourceUnit );
		}

		internal LambdaExpression ParseSourceCode( SourceUnit sourceUnit, CompilerOptions options, ErrorSink errorSink )
		{
			var context = new CompilerContext( sourceUnit, options, errorSink );
			var scanner = new Scanner();
			var parser = new Parser( scanner );
			ParseTree tree = null;

			switch ( context.SourceUnit.Kind )
			{
				case SourceCodeKind.Expression:
					tree = parser.Parse( sourceUnit.GetCode() );
					break;
			}

			if ( tree == null ) return null;

			var builder = Utils.Lambda( typeof( object ), "Excel" );
			builder.Body = Expression.ConvertHelper( tree.GetExpression( this ), typeof( object ) );

			return builder.MakeLambda();
		}

		public override CallAction CreateCallBinder( string name, bool ignoreCase, params Argument[] arguments )
		{
			return new ExcelCallAction( name, ignoreCase, arguments  );
		}

		public override OperationAction CreateOperationBinder( string operation )
		{
			return new OperationBinder( binder, Utils.CodeContext(), operation );
		}
	}
}